////////////////////////////////////////////////////////////////////////////////

#include "client_network.h"

////////////////////////////////////////////////////////////////////////////////

#include <iostream>

////////////////////////////////////////////////////////////////////////////////

#include "timer.h"
#include "mutex.h"

////////////////////////////////////////////////////////////////////////////////


ClientNetwork::ClientNetwork( ServerPackets& _recvQueue,
                              ClientPackets& _sendQueue,
                              Mutex& _sendQueueMutex,
                              Mutex& _recvQueueMutex,
                              const std::string& _address,
                              const std::string& _sendPort,
                              const std::string& _recvPort )
: m_RecvQueue(_recvQueue),
  m_SendQueue(_sendQueue),
  m_RecvQueueMutex(_recvQueueMutex),
  m_SendQueueMutex(_sendQueueMutex),
  m_SendThread(NULL),
  m_RecvThread(NULL)
{
    WSADATA wsaData;
    SOCKET ConnectSocket = INVALID_SOCKET;
    struct addrinfo *result = NULL,
        *ptr = NULL,
        hints;
    
    int iResult;

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);

    ZeroMemory( &hints, sizeof(hints) );
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    // Resolve the server address and port
    iResult = getaddrinfo("localhost", _recvPort.c_str(), &hints, &result);

    // Attempt to connect to an address until one succeeds
    for(ptr=result; ptr != NULL ;ptr=ptr->ai_next)
    {
        // Create a SOCKET for connecting to server
        m_RecvSocket = socket(ptr->ai_family, ptr->ai_socktype, 
            ptr->ai_protocol);

        // Connect to server.
        iResult = connect( m_RecvSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
    }

    // Resolve the server address and port
    iResult = getaddrinfo("localhost", _sendPort.c_str(), &hints, &result);

    // Attempt to connect to an address until one succeeds
    for(ptr=result; ptr != NULL ;ptr=ptr->ai_next)
    {
        // Create a SOCKET for connecting to server
        m_SendSocket = socket(ptr->ai_family, ptr->ai_socktype, 
            ptr->ai_protocol);

        // Connect to server.
        iResult = connect( m_SendSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
    }

    m_SendThread = new Thread(&ClientNetwork::Send, this);
    m_RecvThread = new Thread(&ClientNetwork::Receive, this);
}


////////////////////////////////////////////////////////////////////////////////


ClientNetwork::~ClientNetwork()
{
    if (m_SendThread)
    {
        delete m_SendThread;
        shutdown(m_SendSocket, SD_SEND);
        closesocket(m_SendSocket);
    }

    if (m_RecvThread)
    {
        delete m_RecvThread;
        shutdown(m_RecvSocket, SD_RECEIVE);
        closesocket(m_RecvSocket);
    }
}


////////////////////////////////////////////////////////////////////////////////


void ClientNetwork::Send()
{
    bool continueSending = true;
    while (continueSending)
    {
        m_SendQueueMutex.Lock();
        while ( !m_SendQueue.empty() && continueSending )
        {
            ClientPacket& packet = m_SendQueue.back();

            // Send packet
            int sendResult = send( m_SendSocket, &packet.ToStr()[0], packet.Size(), 0 );
            continueSending = (sendResult != SOCKET_ERROR);

            m_SendQueue.pop();
        }

        m_SendQueueMutex.Unlock();

        // Delay
        Timer::Delay(1);
    }
}


////////////////////////////////////////////////////////////////////////////////


void ClientNetwork::Receive()
{
    bool continueReceiving = true;
    while (continueReceiving)
    {
        const int bufSize = 100;
        char buf[bufSize];
        int bytesReceived = recv( m_RecvSocket, buf, bufSize, 0);

        // First 2 bytes is packet size
        short* packetSizePtr = reinterpret_cast<short*>(buf);
        short packetSize = *packetSizePtr;
        continueReceiving = ( bytesReceived == packetSize + sizeof(short) );

        if (continueReceiving)
        {
            // Server packet
            ServerPacket packet;
            packet.FromStr( buf + sizeof(short) );

            m_RecvQueueMutex.Lock();
            m_RecvQueue.push(packet);
            m_RecvQueueMutex.Unlock();

            // Delay
            Timer::Delay(1);
        }
    }
}


////////////////////////////////////////////////////////////////////////////////
